import os, sys
from Sanae3D.lib.ModelObject import Model3D

class TxtX_Parser(Model3D):
    '''.x text file format'''
    
    #hardcoding all templates I've seen so far. Haven't found a way to
    #skip over the definition of each template
    
    templates = set(("Frame", "FrameTransformMatrix", "Mesh", "MeshNormals", 
                 "MeshTextureCoords", "XSkinMeshHeader", "SkinWeights",
                 "MeshMaterialList", "Material", "TextureFilename", "template",
                 "AnimTicksPerSecond", "MeshFace", "MeshVertexColors",
                 "Animation", "AnimationSet", "TimedFloatKeys",
                 "AnimationKey", "VertexDuplicationIndices", "Header")
                 )
    
    def __init__(self, obj3D=None, inFile=None, outFile=None):
        
        super(TxtX_Parser, self).__init__("X")
        self.obj3D = obj3D
        self.inFile = inFile
        self.outFile = outFile
        self.frameStack = []
        self.tempStack = []
        self.tempCount = 0
        self.xMats = []
        self.matReference = False
        
    def start_temp(self, template):
        
        self.tempStack.append(template)
        
    def get_temp(self):
        
        return self.tempStack[-1]
        
    def end_temp(self):
        
        self.tempStack.pop()
        
    def skip_blank(self):
        
        line = self.inFile.readline().strip()
        while not line or line == "{" or line == "}":
            line = self.inFile.readline()
        return line
    
    def parse_normals(self, objName):
        
        line = self.skip_blank()
        normCount = int(line.strip().strip(';'))

        if normCount == self.vert_count(objName):        
            for i in range(0, normCount):
                line = self.inFile.readline().strip().strip(',').strip(';')
                norms = line.split(";")
                
                self.add_vert_normals(objName, i, norms)
        else:
            for i in range(0, normCount):
                line = self.inFile.readline().strip().strip(',').strip(';')
    
    def parse_texture_coords(self, objName):
        
        line = self.skip_blank()
        texCount = int(line.strip().strip(';'))
        
        if texCount == self.vert_count(objName):
            for i in range(0, texCount):
                line = self.inFile.readline().strip().strip(',').strip(';')
                uv = line.split(";")
                
                self.add_vert_uv(objName, i, uv)
        else:
            for i in range(0, texCount):
                line = self.inFile.readline().strip().strip(',').strip(';')
                print line
    
    def parse_texture(self, matNum):
        
        line = self.inFile.readline()
        texName = line.strip().strip(";").replace('"', "")
        self.add_texture_name(matNum, texName)
    
    def parse_material(self, line):
        
        line = line.replace("{", "").split()
        matNum = self.mat_count()
        matName = line[1] if len(line) > 1 else \
                "material" + str(matNum)
        
        self.xMats.append(matName)

        
        rgba = self.inFile.readline().strip().strip(";;").split(";")
        power = self.inFile.readline().strip().strip(";")
        specular = self.inFile.readline().strip().strip(";;").split(";")
        emissive = self.inFile.readline().strip().strip(";;").split(";")
        
        self.create_material(matNum)
        self.add_material_name(matNum, matName)
        self.add_rgba(matNum, rgba)
        self.add_power(matNum, power)
        self.add_specular(matNum, specular)
        self.add_emissive(matNum, emissive)
        return matNum
    
    def parse_material_list(self, objName):
        
        matOffset = self.mat_count()
        
        #keep track of the material numbers
        matNumbers = []
        
        line = self.inFile.readline().strip().strip(";").split(";")
        
        #different ways to define material list
        
        if len(line) == 3:
            #matCount; FaceCount; MatNum;;
            matCount = int(line[0])
            
            faceCount = self.face_count(objName)
            matNum = int(line[2])
            
            for i in range(0, faceCount):
                self.add_face_material(objName, i, matNum)
        else:
            
            matCount = int(line[0])
            faceCount = int(self.inFile.readline().strip().strip(";"))
            #two cases: and without new line
            line = self.inFile.readline().strip().strip(',').strip(';')
            
            if len(line) == 1:                
                if faceCount != 1:
                    matNum = int(line.strip().strip(',').strip(';'))
                    matNumbers.append(matNum)
                    for i in range(1, faceCount):
                        line = self.inFile.readline()
                        matNum = int(line.strip().strip(',').strip(';'))
                        matNumbers.append(matNum)
                        #self.add_face_material(objName, i, matNum)
                else:
                    faceCount = self.face_count(objName)
                    matNum = int(line.strip().strip(',').strip(';'))
                    for i in range(0, faceCount):
                        matNumbers.append(matNum)
                        #self.add_face_material(objName, i, matNum)
            else:
                x = 0
                numbers = line.split(',')
                for matNum in numbers:
                    #self.add_face_material(objName, x, matNum)
                    matNumbers.append(matNum)
                    x += 1
                while x != faceCount:
                    line = self.inFile.readline().strip().strip(',').strip(';')
                    numbers = line.split(',')
                    for matNum in numbers:
                        matNumbers.append(matNum)
                        x += 1
                        
            #some x files define materials first
            if self.matReference:
                #get the materials used
                matUsed = []
                for i in range(matCount):
                    line = self.inFile.readline().strip()                
                    name = line[1:-1]
                    matIndex = self.xMats.index(name)
                    matUsed.append(matIndex)
                    
                #assign face material with updated index
                for i in range(faceCount):
                    self.add_face_material(objName, i, matUsed[int(matNumbers[i])])
            else:
                for i in range(faceCount):
                    self.add_face_material(objName, i, matNumbers[i])
            
            
    def parse_mesh(self, line):
        
        line = line.replace("{", "").split()
        objName = line[1] if len(line) > 1 else "obj" + str(len(self.get_objects()))
        self.create_object(objName)
        
        line = self.skip_blank()
        meshCount = int(line.strip().strip(";"))
        for i in range(0, meshCount):
            line = self.inFile.readline().strip().strip(";,")
            coords = line.split(";")
            
            coords[0] = float(coords[0]) * -1.0
            
            self.create_vertex(objName, i)
            self.add_coords(objName, i, coords)
        line = self.skip_blank()
        faceCount = int(line.replace(";", "").strip())
        
        for i in range(0, faceCount):
            line = self.inFile.readline().strip().strip(";,").split(";")
            numVerts = int(line[0])
            
            tempVerts = line[1:]
            if len(tempVerts) == numVerts:
                verts = tempVerts
            else:
                #uses commas to separate vertices
                verts = tempVerts[0].split(',')

            self.create_face(objName, i)
            self.add_face_verts(objName, i, verts)
        
        return objName            

    def parse_file(self):
        
        line = self.inFile.readline()
        while line:
            temp = line.strip().split()
            if not temp: 
                pass
            else:
                template = temp[0].strip("{")
                if template in self.templates:
                    #use template definition to check material referencing
                    if template == "Template" and temp[1] == "Material":
                        self.matReference = True
                    self.start_temp(template)
                    
                if line.find("{") != -1 and line.find("}") == -1:
                    #get current template
                    template = self.get_temp()
                    if template == "Frame":
                        pass
                    elif template == "SkinWeights":
                        pass
                    elif template == "Mesh":
                        objName = self.parse_mesh(line)
                    elif template == "MeshTextureCoords":
                        self.parse_texture_coords(objName)
                    elif template == "MeshNormals":                    
                        self.parse_normals(objName)
                    elif template == "MeshMaterialList":
                        self.parse_material_list(objName)
                    elif template == "Material":
                        matNum = self.parse_material(line)
                    elif template == "TextureFilename":
                        self.parse_texture(matNum)
                elif line.find("}") != -1 and line.find("{") == -1:
                    self.end_temp()      
            line = self.inFile.readline()
        
    #===Export Methods===
    
    def write_header(self):
        '''Writes header, assuming 32-bit, version 0303txt with arbitrary
        templates defined'''
        
        self.outFile.write("xof 0303txt 0032\n")
        self.outFile.write("template Header {\n")
        self.outFile.write(" <3D82AB43-62DA-11cf-AB39-0020AF71E433>\n")
        self.outFile.write(" WORD major;\n")
        self.outFile.write(" WORD minor;\n")
        self.outFile.write(" DWORD flags;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Vector {\n")
        self.outFile.write(" <3D82AB5E-62DA-11cf-AB39-0020AF71E433>\n")
        self.outFile.write(" FLOAT x;\n")
        self.outFile.write(" FLOAT y;\n")
        self.outFile.write(" FLOAT z;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Coords2d {\n")
        self.outFile.write(" <F6F23F44-7686-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" FLOAT u;\n")
        self.outFile.write(" FLOAT v;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Matrix4x4 {\n")
        self.outFile.write(" <F6F23F45-7686-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" array FLOAT matrix[16];\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template ColorRGBA {\n")
        self.outFile.write(" <35FF44E0-6C7C-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" FLOAT red;\n")
        self.outFile.write(" FLOAT green;\n")
        self.outFile.write(" FLOAT blue;\n")
        self.outFile.write(" FLOAT alpha;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template ColorRGB {\n")
        self.outFile.write(" <D3E16E81-7835-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" FLOAT red;\n")
        self.outFile.write(" FLOAT green;\n")
        self.outFile.write(" FLOAT blue;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template IndexedColor {\n")
        self.outFile.write(" <1630B820-7842-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD index;\n")
        self.outFile.write(" ColorRGBA indexColor;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Boolean {\n")
        self.outFile.write(" <4885AE61-78E8-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" WORD truefalse;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Boolean2d {\n")
        self.outFile.write(" <4885AE63-78E8-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" Boolean u;\n")
        self.outFile.write(" Boolean v;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MaterialWrap {\n")
        self.outFile.write(" <4885AE60-78E8-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" Boolean u;\n")
        self.outFile.write(" Boolean v;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template TextureFilename {\n")
        self.outFile.write(" <A42790E1-7810-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" STRING filename;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Material {\n")
        self.outFile.write(" <3D82AB4D-62DA-11cf-AB39-0020AF71E433>\n")
        self.outFile.write(" ColorRGBA faceColor;\n")
        self.outFile.write(" FLOAT power;\n")
        self.outFile.write(" ColorRGB specularColor;\n")
        self.outFile.write(" ColorRGB emissiveColor;\n")
        self.outFile.write(" [...]\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshFace {\n")
        self.outFile.write(" <3D82AB5F-62DA-11cf-AB39-0020AF71E433>\n")
        self.outFile.write(" DWORD nFaceVertexIndices;\n")
        self.outFile.write(" array DWORD faceVertexIndices[nFaceVertexIndices];\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshFaceWraps {\n")
        self.outFile.write(" <4885AE62-78E8-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD nFaceWrapValues;\n")
        self.outFile.write(" Boolean2d faceWrapValues;\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshTextureCoords {\n")
        self.outFile.write(" <F6F23F40-7686-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD nTextureCoords;\n")
        self.outFile.write(" array Coords2d textureCoords[nTextureCoords];\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshMaterialList {\n")
        self.outFile.write(" <F6F23F42-7686-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD nMaterials;\n")
        self.outFile.write(" DWORD nFaceIndexes;\n")
        self.outFile.write(" array DWORD faceIndexes[nFaceIndexes];\n")
        self.outFile.write(" [Material]\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshNormals {\n")
        self.outFile.write(" <F6F23F43-7686-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD nNormals;\n")
        self.outFile.write(" array Vector normals[nNormals];\n")
        self.outFile.write(" DWORD nFaceNormals;\n")
        self.outFile.write(" array MeshFace faceNormals[nFaceNormals];\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template MeshVertexColors {\n")
        self.outFile.write(" <1630B821-7842-11cf-8F52-0040333594A3>\n")
        self.outFile.write(" DWORD nVertexColors;\n")
        self.outFile.write(" array IndexedColor vertexColors[nVertexColors];\n")
        self.outFile.write("}\n\n")
        
        self.outFile.write("template Mesh {\n")
        self.outFile.write(" <3D82AB44-62DA-11cf-AB39-0020AF71E433>\n")
        self.outFile.write(" DWORD nVertices;\n")
        self.outFile.write(" array Vector vertices[nVertices];\n")
        self.outFile.write(" DWORD nFaces;\n")
        self.outFile.write(" array MeshFace faces[nFaces];\n")
        self.outFile.write(" [...]\n")
        self.outFile.write("}\n\n")
        
    def write_vertices(self, meshName):
        
        vertList = self.obj3D.get_vertex_list(meshName)
        numVerts = len(vertList)
        self.outFile.write("\t%d;\n" %numVerts)
        for i in range(numVerts - 1):
            coords = self.obj3D.get_coords(meshName, vertList[i])
            self.outFile.write("\t%s;%s;%s;,\n" %(coords[0], coords[1], coords[2]))
        coords = self.obj3D.get_coords(meshName, vertList[numVerts-1])
        self.outFile.write("\t%s;%s;%s;;\n" %(coords[0], coords[1], coords[2]))
            
        faceList = self.obj3D.get_faces(meshName)
        numFaces = len(faceList)
        self.outFile.write("\n\t%d;\n" %numFaces)
        for faceNum in faceList:
            verts = self.obj3D.get_face_verts(meshName, faceNum)
            vertCount = len(verts)
            self.outFile.write("\t%d;%d" %(vertCount, verts[0]))
            for i in range(1, vertCount):
                self.outFile.write(",%d" %verts[i])
            self.outFile.write(";,\n")
        
    def write_mesh(self):
        
        meshes = self.obj3D.get_objects()
        for meshName in meshes:
            self.outFile.write("Mesh %s {\n" %meshName)
            self.write_vertices(meshName)
            self.outFile.write("}\n")
        
    def print_file(self):
        
        self.write_header()
        self.write_mesh()
                
def read_file(path):
    
    openFile = open(path, 'r')
    obj = TxtX_Parser(inFile=openFile)
    obj.parse_file()
    openFile.close()
    return obj

def write_file(name, modelObj):
    
    openFile = open(name + ".x", 'w')
    obj = TxtX_Parser(obj3D=modelObj, outFile=openFile)
    obj.print_file()
    openFile.close()

if __name__ == '__main__':
    
    name1 = "efc_fate_beem.x"
    name2 = "act_taiki.x"
    name3 = "C38b_02.x"
    name4 = "eienTei.x"
    name5 = "knife.x"
    obj = read_file(name4)
    write_file(obj, "out")